`include "IF_AHB_paras.vh"

module IF2ITCM(
    input           clk,
    input           rst_n,
    
    /* 1. ID branch */
    input           if_jump,
    input [31:0]    prdt_pc_add_op1,
    input [31:0]    prdt_pc_add_op2,
    
    /* 2. L/S instr */
    input           d_hready,
    
    /* 3. DIV instr */
    input           div_alu_time,
    
    /* 4. data hazard */
    input           wr_stop,
    
    /* 5. interrupt  */
    output [31:0]   normal_PC,
    input           trap_entry_en,
    input           trap_exit_en,
    input [31:0]    trap_entry_pc,
    input [31:0]    restore_pc,
    
    /* 5. ahb master */
    output reg         i_hbusreq,
    output reg [1:0]    i_htrans, /* IDLE, NONSEQ */
    output reg [31:0]   i_haddr,
    output              i_hwrite,
    output     [63:0]   i_hwdata,
    output     [2:0]    i_hsize, /* 3'b011 */
    output     [2:0]    i_hburst,/* 3'b000 */
    output     [2:0]    i_hprot,/* 3'b010 */
    input           i_hgrant,
    input           i_hready,
    input  [1:0]    i_hresp,
    input [63:0]    i_hrdata,
    
    output reg [31:0] instr1,
    output reg instr1_vld,
    output reg [31:0] instr2,
    output reg instr2_vld   
);


localparam AHB_BUS_REQ = 2'b01;
localparam AHB_BUS_TRANS = 2'b10;
localparam AHB_BUS_DATA = 2'b11;

reg [1:0]ahb_state;
reg [1:0]ahb_state_nx;

reg d_itcm; /* access ITCM */
reg d_itcm_reg;

always@(posedge clk or negedge rst_n) begin
    if(~rst_n) begin
        ahb_state <= AHB_BUS_REQ;
        d_itcm_reg <= 1'b1;
    end
    else begin
        ahb_state <= ahb_state_nx;
        d_itcm_reg <= d_itcm;
    end
end



reg [31:0] PC;
reg [31:0] PC_r;

reg start;
reg start_2;

wire [31:0] Add2PC_op2 = if_jump ? prdt_pc_add_op2 : `PC_add_insr;
wire [31:0] Add2PC_op1 = if_jump ? prdt_pc_add_op1 : PC_r;

assign normal_PC= PC;
always@(*) begin
    if(|{wr_stop, div_alu_time, ~d_hready, d_itcm_reg}/*|{wr_stop, div_alu_time, ~i_hready, ~d_hready, d_itcm}*/) begin
        PC = PC_r;
    end
    else if(start_2) begin
        PC = Add2PC_op1 + Add2PC_op2;
    end
    else begin
        PC = `ADDRESS_rst;
    end
end
//assign PC = |{wr_stop, div_alu_time, ~i_hready, ~d_hready, d_itcm} ? PC_r : (start_2 ? (Add2PC_op1 + Add2PC_op2) : `ADDRESS_rst );

wire [31:0] PC_cur;
assign PC_cur = trap_entry_en ? trap_entry_pc : trap_exit_en  ? restore_pc : PC;

always@(posedge clk or negedge rst_n) begin
    if(~rst_n) begin
        PC_r <= `ADDRESS_rst;        
        start <= 1'b0;
        start_2 <= 1'b0;
    end
    else begin
        PC_r <= PC_cur;        
        start <= 1'b1;
        start_2 <= start;
    end
end

always@(*) begin
    i_hbusreq = 1'b0;
    i_htrans = `TRANS_IDLE;
    i_haddr = PC_cur;
    ahb_state_nx = ahb_state;
    d_itcm = d_itcm_reg;
    instr1[31:0] = 'd0;
    instr1_vld = 1'b0;
    instr2[31:0] = 'd0;
    instr2_vld = 1'b0;
    
    case(ahb_state)
        AHB_BUS_REQ: begin
           if(start_2) begin
                i_hbusreq = 1'b1;
                if(i_hgrant == 1'b1) begin
                    ahb_state_nx = AHB_BUS_TRANS;
                    //d_itcm = 1'b0;
                    i_hbusreq = 1'b0;
                end
                else begin
                    //d_itcm = 1'b1;
                end
           end 
        end
        AHB_BUS_TRANS: begin
            i_htrans = `TRANS_NONSEQ;
            i_haddr = PC_cur;
            ahb_state_nx = AHB_BUS_DATA;
            d_itcm = 1'b0;            
        end
        AHB_BUS_DATA: begin
            if(i_hready & (i_hresp == `RESP_OKAY)) begin
                instr1[31:0] = i_hrdata[31:0];
                instr1_vld = 1'b1;
                instr2[31:0] = i_hrdata[63:32];
                instr2_vld = 1'b1;
                ahb_state_nx = AHB_BUS_REQ;
            end
            d_itcm = 1'b1;
        end
        default: begin
            i_hbusreq = 1'b0;
            i_htrans = `TRANS_IDLE;
            i_haddr = PC_cur;
            ahb_state_nx = ahb_state;
            d_itcm = d_itcm_reg;
            instr1[31:0] = 'd0;
            instr1_vld = 1'b0;
            instr2[31:0] = 'd0;
            instr2_vld = 1'b0;        
        end
    endcase
end


assign i_hsize[2:0] = `HSIZE;
assign i_hburst[2:0] = `HBURST;
assign i_hprot[2:0] = `HPROT;
assign i_hwrite = 1'b0;
assign i_hwdata = 32'b0;

endmodule
